package com.example.boot.security.filter;

import com.auth0.jwt.JWT;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.example.boot.security.handler.LoginFailureHandler;
import com.example.boot.service.impl.CustomerUserDetailsServiceImpl;
import com.example.boot.utils.MyAuthenticationException;
import com.example.boot.utils.RedisService;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;


/**
 * @description:
 * @author: mhpy
 * @date 2022/8/7 20:10
 */
@Component
public class CheckTokenFilter extends OncePerRequestFilter {

    @Resource
    private RedisService redisService;
    @Resource
    private CustomerUserDetailsServiceImpl detailsService;
    @Resource
    private LoginFailureHandler loginFailureHandler;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String requestUri = request.getRequestURI();
            if (!requestUri.equals("/api/user/login")){
                this.validateToken(request);
            }
        } catch (AuthenticationException e) {
            loginFailureHandler.onAuthenticationFailure(request,response,e);
        }
//        登录请求直接放行
        doFilter(request,response,filterChain);
    }

//  token验证

    private void validateToken(HttpServletRequest request) {
        String token=request.getHeader("X-Token");
        if (ObjectUtils.isEmpty(token)){
            throw new InternalAuthenticationServiceException("token不存在");
        }
//        判断redis是否存在token ， 不存在则token失效
        String redisToken = redisService.get("token_" + token);
        if(StringUtils.isEmpty(redisToken)){
            throw new MyAuthenticationException("token失效,redis 不存在此token");
        }
//        请求头token 与 redis token 不一致 则验证失败
        if (!Objects.equals(token, redisToken)){
            throw new MyAuthenticationException("token验证失败，token 不一致");
        }
//        token 验证成功 则解析token
        String name= JWT.decode(token).getAudience().get(0);
        if (StringUtils.isEmpty(name)){
            throw new MyAuthenticationException("token解析失败");
        }
//      获取用户信息
        UserDetails userDetails = detailsService.loadUserByUsername(name);
        if (userDetails==null){
            throw new MyAuthenticationException("认证失败");
        }
//        用户认证对象
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
//        设置请求信息
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
//        将验证信息交给spring security 上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    }
}
